The world cup 2022 will be hosted by Qatar in 2022. We wanted to try
and predict which country has the biggest probability of winning the
tournament by using supervised learning.
2. EDA
# class of each variable
spec(input_data)
cols(
date = col_date(format = ""),
home_team = col_character(),
away_team = col_character(),
home_team_continent = col_character(),
away_team_continent = col_character(),
home_team_fifa_rank = col_double(),
away_team_fifa_rank = col_double(),
home_team_total_fifa_points = col_double(),
away_team_total_fifa_points = col_double(),
home_team_score = col_double(),
away_team_score = col_double(),
tournament = col_character(),
city = col_character(),
country = col_character(),
neutral_location = col_logical(),
shoot_out = col_character(),
home_team_result = col_character(),
home_team_goalkeeper_score = col_double(),
away_team_goalkeeper_score = col_double(),
home_team_mean_defense_score = col_double(),
home_team_mean_offense_score = col_double(),
home_team_mean_midfield_score = col_double(),
away_team_mean_defense_score = col_double(),
away_team_mean_offense_score = col_double(),
away_team_mean_midfield_score = col_double()
)
# summary
skim_without_charts(input_data)
── Data Summary ────────────────────────
Values
Name input_data
Number of rows 23921
Number of columns 25
_______________________
Column type frequency:
character 9
Date 1
logical 1
numeric 14
________________________
Group variables None
Missing data
input_data %>%
summarise_all(list(~is.na(.)))%>%
pivot_longer(everything(),
names_to = "variables", values_to="missing") %>%
count(variables, missing) %>%
ggplot(aes(y=variables,x=n,fill=missing))+
geom_col()+
scale_fill_manual(values=c("#A3BE8C","#EBCB8B"))+
theme(axis.title.y=element_blank())

Top 10 teams in 2022
# Get the ranking of all home teams
home <-
input_data %>%
select(date, home_team, home_team_fifa_rank) %>%
rename(team = home_team, ranking = home_team_fifa_rank)
# Get the ranking of all away teams
away <-
input_data %>%
select(date, away_team, away_team_fifa_rank) %>%
rename(team = away_team, ranking = away_team_fifa_rank)
# Combine both data frames into one
fifa_ranking <- rbind(home, away)
# Get the latest ranking of each country based on their most recent match
latest_fifa_ranking <-
fifa_ranking %>%
arrange(team, desc(date)) %>%
group_by(team) %>%
mutate(row_number = row_number(team)) %>%
filter(row_number == 1) %>%
select(-row_number, -date) %>%
arrange(ranking)
head(latest_fifa_ranking, 10)
FIFA rankings over time
top5_list <- head(latest_fifa_ranking, 5)$team
top5_ranking <-
fifa_ranking %>%
filter(team %in% top5_list)
p <-
ggplot(data = top5_ranking,
mapping = aes(
x = date,
y = ranking,
group = team,
color = team
)) +
geom_line() +
scale_y_reverse() +
labs(
x = "Date",
y = "FIFA Ranking",
color = "Team",
title = "FIFA Rankings of the 2022 Top 5 teams"
)
ggplotly(p)
NA
Teams with strongest GK
# Gather goalkeeper data from matches
gk_home <-
input_data %>%
select(date, home_team, home_team_goalkeeper_score) %>%
rename(team = home_team, goalkeeper_rating = home_team_goalkeeper_score)
gk_away <-
input_data %>%
select(date, away_team, away_team_goalkeeper_score) %>%
rename(team = away_team, goalkeeper_rating = away_team_goalkeeper_score)
gk_rating <- drop_na(rbind(gk_home, gk_away))
# Get latest rating of each team's goalkeeper and show top 10
latest_gk_rating <-
gk_rating %>%
arrange(team, desc(date)) %>%
group_by(team) %>%
mutate(row_number = row_number(team)) %>%
filter(row_number == 1) %>%
select(-row_number, -date) %>%
arrange(-goalkeeper_rating)
ggplot(data = head(latest_gk_rating, 10), mapping = aes(x=goalkeeper_rating, y=reorder(team, goalkeeper_rating), label=goalkeeper_rating)) +
geom_col(fill="#88C0D0") +
geom_text(position = position_stack(vjust = 0.5)) +
labs(title = "Top 10 teams with the strongest goalkeeper",
subtitle = "Based on the highest rated goalkeeper of each team",
x="Goalkeeper Rating",
y="Country")

Teams with strongest defense
# Gather goalkeeper and defense data from matches
def_home <-
input_data %>%
select(date, home_team, home_team_goalkeeper_score, home_team_mean_defense_score) %>%
rename(team = home_team, goalkeeper_rating = home_team_goalkeeper_score, mean_defense_rating = home_team_mean_defense_score)
def_away <-
input_data %>%
select(date, away_team, away_team_goalkeeper_score, away_team_mean_defense_score) %>%
rename(team = away_team, goalkeeper_rating = away_team_goalkeeper_score, mean_defense_rating = away_team_mean_defense_score)
def_rating <- drop_na(rbind(def_home, def_away))
# Get latest combined rating of each team and show top 10
latest_def_rating <-
def_rating %>%
arrange(team, desc(date)) %>%
mutate(total_def = goalkeeper_rating + mean_defense_rating) %>%
group_by(team) %>%
mutate(row_number = row_number(team)) %>%
filter(row_number==1) %>%
arrange(-total_def) %>%
select(-row_number, -date)
ggplot(data = head(latest_def_rating, 10), mapping=aes(x=total_def, y=reorder(team, total_def), label=total_def)) +
geom_col(fill="#88C0D0") +
geom_text(position = position_stack(vjust = 0.5)) +
labs(title = "Top 10 teams with the strongest defense",
subtitle = "Based on goalkeeper and mean defense ratings",
x = "Total Defense Rating",
y = "Teams")

Teams with strongest midfield
mid_home <-
input_data %>%
select(date, home_team, home_team_mean_midfield_score) %>%
rename(team = home_team, midfield_rating = home_team_mean_midfield_score)
mid_away <-
input_data %>%
select(date, away_team, away_team_mean_midfield_score) %>%
rename(team = away_team, midfield_rating = away_team_mean_midfield_score)
mid_rating <- drop_na(rbind(mid_home, mid_away))
# Get latest midfield rating of each team and show top 10
latest_mid_rating <-
mid_rating %>%
arrange(team, desc(date)) %>%
group_by(team) %>%
mutate(row_number = row_number(team)) %>%
filter(row_number == 1) %>%
arrange(-midfield_rating) %>%
select(-date, -row_number)
ggplot(data = head(latest_mid_rating, 10), mapping=aes(x=midfield_rating, y=reorder(team, midfield_rating), label=midfield_rating)) +
geom_col(fill= "#88C0D0") +
geom_text(position = position_stack(vjust = 0.5)) +
labs(title = "Top 10 teams with the strongest midfield",
subtitle = "Based on the average rating of the 4 highest rated midfield players of each team",
x = "Midfield Rating",
y = "Teams")

Teams with strongest offense
off_home <-
input_data %>%
select(date, home_team, home_team_mean_offense_score) %>%
rename(team = home_team, offense_rating = home_team_mean_offense_score)
off_away <-
input_data %>%
select(date, away_team, away_team_mean_offense_score) %>%
rename(team = away_team, offense_rating = away_team_mean_offense_score)
off_rating <- drop_na(rbind(off_home, off_away))
# Get latest offense rating of each team and show top 10
latest_off_rating <-
off_rating %>%
arrange(team, desc(date)) %>%
group_by(team) %>%
mutate(row_number = row_number(team)) %>%
filter(row_number == 1) %>%
arrange(-offense_rating) %>%
select(-date, -row_number)
ggplot(data = head(latest_off_rating, 10), mapping=aes(x=offense_rating, y=reorder(team, offense_rating), label=offense_rating)) +
geom_col(fill="#88C0D0") +
geom_text(position = position_stack(vjust = 0.5)) +
labs(title="Top 10 teams with the strongest offense",
subtitle="Based on the average rating of the 3 highest rated offensive players of each team",
x="Offense Rating",
y="Teams")

Is it better to play at home ?
home_team_advantage <-
input_data %>%
filter(neutral_location == FALSE) %>%
count(home_team_result) %>%
mutate(percentage = label_percent()(n/sum(n)))
ggplot(data = home_team_advantage, mapping=aes(x="", y=n, fill=home_team_result)) +
geom_bar(width = 1, stat = "identity", color="white") +
coord_polar("y") +
scale_fill_manual(values = c("#EBCB8B", "#BF616A",
"#A3BE8C")) +
theme_void() +
labs(title = "Distribution of match results of home teams",
subtitle = "Excluding matches played at neutral locations",
fill="Result")

Correlation Matrix
# select numeric columns only
input_numeric_data <- input_data %>%
select_if(is.numeric) %>%
drop_na()
# rename variables for easier correlation plot visualization
input_numeric_data <- input_numeric_data %>% rename(
rank1 = home_team_fifa_rank,
rank2 = away_team_fifa_rank,
total_fifa_points1 = home_team_total_fifa_points,
total_fifa_points2 = away_team_total_fifa_points,
score1 = home_team_score,
score2 = away_team_score,
gk_score1 = home_team_goalkeeper_score,
gk_score2 = away_team_goalkeeper_score,
df_score1 = home_team_mean_defense_score,
df_score2 = away_team_mean_defense_score,
att_score1 = home_team_mean_offense_score,
att_score2 = away_team_mean_offense_score,
mf_score1 = home_team_mean_midfield_score,
mf_score2 = away_team_mean_midfield_score
)
# create correlation plot
input_numeric_data %>%
cor() %>%
corrplot(
type = "upper",
diag = FALSE,
col=colorRampPalette(c("firebrick","lightyellow","green4"))(100),
method = "shade",
shade.col = NA,
tl.col = "black",
tl.srt = 45
)

2. Data Processing / Feature Engineering
Create new features
output_data$win <- output_data$score_difference > 0
Warning: Unknown or uninitialised column: `score_difference`.
Error:
! Assigned data `output_data$score_difference > 0` must be compatible with existing data.
✖ Existing data has 23921 rows.
✖ Assigned data has 0 rows.
ℹ Only vectors of size 1 are recycled.
Backtrace:
1. base::`$<-`(`*tmp*`, win, value = `<lgl>`)
12. tibble (local) `<fn>`(`<vctrs___>`)
Model
# create training and test set
sample <- sample(c(TRUE, FALSE), nrow(output_data), replace=TRUE, prob=c(0.7,0.3))
train <- output_data[sample, ]
test <- output_data[!sample, ]
# fit logistic regression model
logreg <- glm(win ~ average_rank + rank_diff + point_diff, family = "binomial", data = train)
summary(logreg)
Call:
glm(formula = win ~ average_rank + rank_diff + point_diff, family = "binomial",
data = train)
Deviance Residuals:
Min 1Q Median 3Q Max
-2.6642 -1.0028 -0.3857 1.0260 2.5995
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -0.2726775 0.0330011 -8.263 < 2e-16 ***
average_rank 0.0018669 0.0003617 5.161 2.46e-07 ***
rank_diff -0.0196047 0.0004886 -40.120 < 2e-16 ***
point_diff 0.0003186 0.0001322 2.410 0.016 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 23081 on 16657 degrees of freedom
Residual deviance: 19729 on 16654 degrees of freedom
AIC: 19737
Number of Fisher Scoring iterations: 4
# calc probability of win for each team in test dataset
predicted <- predict(logreg, test, type="response")
# convert wins to 1 and 0
test$win <- ifelse(test$win==TRUE, 1, 0)
# find optimal cutoff probability to use to maximize accuracy
optimal <- optimalCutoff(test$win, predicted)[1]
optimal
[1] 0.5171179
# confusion matrix
confusionMatrix(test$win, predicted)
# calculate miss classification error rate
misClassError(test$win, predicted, threshold = optimal)
[1] 0.3192
# ROC
plotROC(test$win, predicted)

Test
index(wc_rankings_away)
Error in index(wc_rankings_away) : could not find function "index"
row <- data.frame(matrix(nrow = 0, ncol = length(colnames(test)))) %>%
colnames(row) <- colnames(test)
Error in data.frame(matrix(nrow = 0, ncol = length(colnames(test)))) %>% :
target of assignment expands to non-language object
LS0tCnRpdGxlOiAiV29ybGQgQ3VwIDIwMjIiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2tpbXIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dhbmltYXRlKQpsaWJyYXJ5KEluZm9ybWF0aW9uVmFsdWUpCmBgYAoKVGhlIHdvcmxkIGN1cCAyMDIyIHdpbGwgYmUgaG9zdGVkIGJ5IFFhdGFyIGluIDIwMjIuIFdlIHdhbnRlZCB0byB0cnkgYW5kIHByZWRpY3QKd2hpY2ggY291bnRyeSBoYXMgdGhlIGJpZ2dlc3QgcHJvYmFiaWxpdHkgb2Ygd2lubmluZyB0aGUgdG91cm5hbWVudCBieSB1c2luZyBzdXBlcnZpc2VkIGxlYXJuaW5nLgoKIyAxLiBEYXRhCgpUaGUgZGF0YSB0aGF0IHdlIHdpbGwgYmUgdXNpbmcgaXMgYWxyZWFkeSBjbGVhbmVkIGFuZCBkb2VzIG5vdCByZXF1aXJlIHByZS1wcm9jZXNzaW5nLgoKVGhlIGRhdGEgc2V0IHByb3ZpZGVzIGEgY29tcGxldGUgb3ZlcnZpZXcgb2YgYWxsIGludGVybmF0aW9uYWwgZm9vdGJhbGwgbWF0Y2hlcyBwbGF5ZWQgc2luY2UgdGhlIDkwcy4gT24gdG9wIG9mIHRoYXQsIHRoZSBzdHJlbmd0aCBvZiBlYWNoIHRlYW0gaXMgcHJvdmlkZWQgYnkgaW5jb3Jwb3JhdGluZyB0aGUgRklGQSByYW5raW5ncyBhcyB3ZWxsIGFzIHBsYXllciBzdHJlbmd0aHMgYmFzZWQgb24gdGhlIEVBIFNwb3J0IEZJRkEgdmlkZW8gZ2FtZS4gSXQgaXMgYXZhaWxhYmxlIG9uIFtrYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvYnJlbmRhODkvZmlmYS13b3JsZC1jdXAtMjAyMikKCiMjIFZhcmlhYmxlcwoKLSBgZGF0ZWAgOiBkYXRlIG9mIHRoZSBtYXRjaAotIGBob21lX3RlYW1gIDogbmFtZSBvZiB0aGUgaG9tZSB0ZWFtCi0gYGF3YXlfdGVhbWAgOiBuYW1lIG9mIHRoZSBhd2F5IHRlYW0KLSBgaG9tZV90ZWFtX2NvbnRpbmVudGAgOiBjb250aW5lbnQgb2YgdGhlIGhvbWUgdGVhbQotIGBhd2F5X3RlYW1fY29udGluZW50YCA6IGNvbnRpbmVudCBvZiB0aGUgYXdheSB0ZWFtCi0gYGhvbWVfdGVhbV9maWZhX3JhbmtgIDogRklGQSByYW5rIG9mIHRoZSBob21lIHRlYW0gd2hlbiB0aGUgbWF0Y2ggdG9vayBwbGFjZQotIGBhd2F5X3RlYW1fZmlmYV9yYW5rYCA6IEZJRkEgcmFuayBvZiB0aGUgYXdheSB0ZWFtIHdoZW4gdGhlIG1hdGNoIHRvb2sgcGxhY2UKLSBgaG9tZV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzYCA6IHRvdGFsIG51bWJlciBvZiBGSUZBIHBvaW50cyBvZiB0aGUgaG9tZSB0ZWFtIGF0IHRoZSB0aW1lIG9mIHRoZSBtYXRjaAotIGBhd2F5X3RlYW1fdG90YWxfZmlmYV9wb2ludHNgIDogdG90YWwgbnVtYmVyIG9mIEZJRkEgcG9pbnRzIG9mIHRoZSBhd2F5IHRlYW0gYXQgdGhlIHRpbWUgb2YgdGhlIG1hdGNoCi0gYGhvbWVfdGVhbV9zY29yZWAgOiBmdWxsLXRpbWUgaG9tZSBzY29yZSAoZXhjbHVkaW5nIHBlbmFsdHkgc2hvb3RvdXQpCi0gYGF3YXlfdGVhbV9zY29yZWAgOiBmdWxsLXRpbWUgYXdheSBzY29yZSAoZXhjbHVkaW5nIHBlbmFsdHkgc2hvb3RvdXQpCi0gYHRvdXJuYW1lbnRgIDogbmFtZSBvZiB0b3VybmFtZW50Ci0gYGNpdHlgIDogbmFtZSBvZiB0aGUgY2l0eSB3aGVyZSB0aGUgbWF0Y2ggd2FzIHBsYXllZAotIGBjb3VudHJ5YCA6IG5hbWUgb2YgdGhlIGNvdW50cnkgd2hlcmUgdGhlIG1hdGNoIHdhcyBwbGF5ZWQKLSBgbmV1dHJhbF9sb2NhdGlvbmAgOgogIC0gYFRSVUVgIDogdGhlIG1hdGNoIHdhcyBwbGF5ZWQgYXQgYSBuZXV0cmFsIHZlbnVlCi0gYHNob290X291dGA6CiAgLSBgVFJVRWAgOiB0aGUgbWF0Y2ggaW5jbHVkZWQgYSBwZW5hbHR5IHNob290b3V0Ci0gYGhvbWVfdGVhbV9yZXN1bHRgIDogcmVzdWx0IG9mIHRoZSBob21lIHRlYW0gKGluY2x1ZGluZyBwZW5hbHR5IHNob290b3V0KQotIGBob21lX3RlYW1fZ29hbGtlZXBlcl9zY29yZWAgOiBGSUZBIGdhbWUgc2NvcmUgb2YgdGhlIGhpZ2hlc3QgcmFua2VkIEdLIG9mIHRoZSBob21lIHRlYW0KLSBgYXdheV90ZWFtX2dvYWxrZWVwZXJfc2NvcmVgIDogRklGQSBnYW1lIHNjb3JlIG9mIHRoZSBoaWdoZXN0IHJhbmtlZCBHSyBvZiB0aGUgYXdheSB0ZWFtCi0gYGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmVgIDogQXZlcmFnZSBGSUZBIGdhbWUgc2NvcmUgb2YgdGhlIDQgaGlnaGVzdCByYW5rZWQgZGVmZW5zaXZlIHBsYXllcnMgb2YgdGhlIGhvbWUgdGVhbQotIGBhd2F5X3RlYW1fbWVhbl9kZWZlbnNlX3Njb3JlYCA6IEF2ZXJhZ2UgRklGQSBnYW1lIHNjb3JlIG9mIHRoZSA0IGhpZ2hlc3QgcmFua2VkIApkZWZlbnNpdmUgcGxheWVycyBvZiB0aGUgYXdheSB0ZWFtCi0gYGhvbWVfdGVhbV9tZWFuX21pZGZpZWxkX3Njb3JlYCA6IEF2ZXJhZ2UgRklGQSBnYW1lIHNjb3JlIG9mIHRoZSA0IGhpZ2hlc3QgcmFua2VkIG1pZGZpZWxkIHBsYXllcnMgb2YgdGhlIGhvbWUgdGVhbQotIGBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZWAgOiBBdmVyYWdlIEZJRkEgZ2FtZSBzY29yZSBvZiB0aGUgNCBoaWdoZXN0IHJhbmtlZCBtaWRmaWVsZCBwbGF5ZXJzIG9mIHRoZSBhd2F5IHRlYW0KLSBgaG9tZV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZWAgOiBBdmVyYWdlIEZJRkEgZ2FtZSBzY29yZSBvZiB0aGUgMyBoaWdoZXN0IHJhbmtlZCBhdHRhY2tpbmcgcGxheWVycyBvZiB0aGUgaG9tZSB0ZWFtLCBpbmNsdWRpbmcgd2luZyBwbGF5ZXJzCi0gYGF3YXlfdGVhbV9tZWFuX29mZmVuc2Vfc2NvcmVgIDogQXZlcmFnZSBGSUZBIGdhbWUgc2NvcmUgb2YgdGhlIDMgaGlnaGVzdCByYW5rZWQgYXR0YWNraW5nIHBsYXllcnMgb2YgdGhlIGF3YXkgdGVhbSwgaW5jbHVkaW5nIHdpbmcgcGxheWVycwoKIyAyLiBFREEKCmBgYHtyfQojIGNsYXNzIG9mIGVhY2ggdmFyaWFibGUKc3BlYyhpbnB1dF9kYXRhKQpgYGAKYGBge3J9CiMgc3VtbWFyeQpza2ltX3dpdGhvdXRfY2hhcnRzKGlucHV0X2RhdGEpCmBgYAoKIyMgTWlzc2luZyBkYXRhCgpgYGB7cn0KaW5wdXRfZGF0YSAlPiUKICBzdW1tYXJpc2VfYWxsKGxpc3QofmlzLm5hKC4pKSklPiUKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZXMiLCB2YWx1ZXNfdG89Im1pc3NpbmciKSAlPiUKICBjb3VudCh2YXJpYWJsZXMsIG1pc3NpbmcpICU+JQogIGdncGxvdChhZXMoeT12YXJpYWJsZXMseD1uLGZpbGw9bWlzc2luZykpKwogIGdlb21fY29sKCkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNBM0JFOEMiLCIjRUJDQjhCIikpKwogIHRoZW1lKGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkpCmBgYAoKCiMjIFRvcCAxMCB0ZWFtcyBpbiAyMDIyCgpgYGB7cn0KIyBHZXQgdGhlIHJhbmtpbmcgb2YgYWxsIGhvbWUgdGVhbXMKaG9tZSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBob21lX3RlYW0sIGhvbWVfdGVhbV9maWZhX3JhbmspICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgcmFua2luZyA9IGhvbWVfdGVhbV9maWZhX3JhbmspCgojIEdldCB0aGUgcmFua2luZyBvZiBhbGwgYXdheSB0ZWFtcwphd2F5IDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgc2VsZWN0KGRhdGUsIGF3YXlfdGVhbSwgYXdheV90ZWFtX2ZpZmFfcmFuaykgJT4lIAogIHJlbmFtZSh0ZWFtID0gYXdheV90ZWFtLCByYW5raW5nID0gYXdheV90ZWFtX2ZpZmFfcmFuaykKCiMgQ29tYmluZSBib3RoIGRhdGEgZnJhbWVzIGludG8gb25lCmZpZmFfcmFua2luZyA8LSByYmluZChob21lLCBhd2F5KQoKIyBHZXQgdGhlIGxhdGVzdCByYW5raW5nIG9mIGVhY2ggY291bnRyeSBiYXNlZCBvbiB0aGVpciBtb3N0IHJlY2VudCBtYXRjaApsYXRlc3RfZmlmYV9yYW5raW5nIDwtCiAgZmlmYV9yYW5raW5nICU+JSAKICBhcnJhbmdlKHRlYW0sIGRlc2MoZGF0ZSkpICU+JSAKICBncm91cF9ieSh0ZWFtKSAlPiUgCiAgbXV0YXRlKHJvd19udW1iZXIgPSByb3dfbnVtYmVyKHRlYW0pKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIgPT0gMSkgJT4lIAogIHNlbGVjdCgtcm93X251bWJlciwgLWRhdGUpICU+JSAKICBhcnJhbmdlKHJhbmtpbmcpCiAgCmhlYWQobGF0ZXN0X2ZpZmFfcmFua2luZywgMTApCmBgYAoKIyMgRklGQSByYW5raW5ncyBvdmVyIHRpbWUKCmBgYHtyfQp0b3A1X2xpc3QgPC0gaGVhZChsYXRlc3RfZmlmYV9yYW5raW5nLCA1KSR0ZWFtCgp0b3A1X3JhbmtpbmcgPC0KICBmaWZhX3JhbmtpbmcgICU+JSAKICBmaWx0ZXIodGVhbSAlaW4lIHRvcDVfbGlzdCkKCnAgPC0KICBnZ3Bsb3QoZGF0YSA9IHRvcDVfcmFua2luZywKICAgICAgICAgbWFwcGluZyA9IGFlcygKICAgICAgICAgICB4ID0gZGF0ZSwKICAgICAgICAgICB5ID0gcmFua2luZywKICAgICAgICAgICBncm91cCA9IHRlYW0sCiAgICAgICAgICAgY29sb3IgPSB0ZWFtCiAgICAgICAgICkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIGxhYnMoCiAgICB4ID0gIkRhdGUiLAogICAgeSA9ICJGSUZBIFJhbmtpbmciLAogICAgY29sb3IgPSAiVGVhbSIsCiAgICB0aXRsZSA9ICJGSUZBIFJhbmtpbmdzIG9mIHRoZSAyMDIyIFRvcCA1IHRlYW1zIgogICkKCmdncGxvdGx5KHApCgpgYGAKCiMjIFRlYW1zIHdpdGggc3Ryb25nZXN0IEdLCgpgYGB7cn0KIyBHYXRoZXIgZ29hbGtlZXBlciBkYXRhIGZyb20gbWF0Y2hlcwpna19ob21lIDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgc2VsZWN0KGRhdGUsIGhvbWVfdGVhbSwgaG9tZV90ZWFtX2dvYWxrZWVwZXJfc2NvcmUpICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgZ29hbGtlZXBlcl9yYXRpbmcgPSBob21lX3RlYW1fZ29hbGtlZXBlcl9zY29yZSkKCmdrX2F3YXkgPC0KICBpbnB1dF9kYXRhICU+JSAKICBzZWxlY3QoZGF0ZSwgYXdheV90ZWFtLCBhd2F5X3RlYW1fZ29hbGtlZXBlcl9zY29yZSkgJT4lIAogIHJlbmFtZSh0ZWFtID0gYXdheV90ZWFtLCBnb2Fsa2VlcGVyX3JhdGluZyA9IGF3YXlfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlKQoKZ2tfcmF0aW5nIDwtIGRyb3BfbmEocmJpbmQoZ2tfaG9tZSwgZ2tfYXdheSkpCgojIEdldCBsYXRlc3QgcmF0aW5nIG9mIGVhY2ggdGVhbSdzIGdvYWxrZWVwZXIgYW5kIHNob3cgdG9wIDEwCmxhdGVzdF9na19yYXRpbmcgPC0KICBna19yYXRpbmcgJT4lIAogIGFycmFuZ2UodGVhbSwgZGVzYyhkYXRlKSkgJT4lIAogIGdyb3VwX2J5KHRlYW0pICU+JSAKICBtdXRhdGUocm93X251bWJlciA9IHJvd19udW1iZXIodGVhbSkpICU+JSAKICBmaWx0ZXIocm93X251bWJlciA9PSAxKSAlPiUgCiAgc2VsZWN0KC1yb3dfbnVtYmVyLCAtZGF0ZSkgJT4lIAogIGFycmFuZ2UoLWdvYWxrZWVwZXJfcmF0aW5nKQoKZ2dwbG90KGRhdGEgPSBoZWFkKGxhdGVzdF9na19yYXRpbmcsIDEwKSwgbWFwcGluZyA9IGFlcyh4PWdvYWxrZWVwZXJfcmF0aW5nLCB5PXJlb3JkZXIodGVhbSwgZ29hbGtlZXBlcl9yYXRpbmcpLCBsYWJlbD1nb2Fsa2VlcGVyX3JhdGluZykpICsKICBnZW9tX2NvbChmaWxsPSIjODhDMEQwIikgKwogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGUgPSAiVG9wIDEwIHRlYW1zIHdpdGggdGhlIHN0cm9uZ2VzdCBnb2Fsa2VlcGVyIiwKICAgICAgIHN1YnRpdGxlID0gIkJhc2VkIG9uIHRoZSBoaWdoZXN0IHJhdGVkIGdvYWxrZWVwZXIgb2YgZWFjaCB0ZWFtIiwKICAgICAgIHg9IkdvYWxrZWVwZXIgUmF0aW5nIiwKICAgICAgIHk9IkNvdW50cnkiKQpgYGAKIyMjIFRlYW1zIHdpdGggc3Ryb25nZXN0IGRlZmVuc2UKCmBgYHtyfQojIEdhdGhlciBnb2Fsa2VlcGVyIGFuZCBkZWZlbnNlIGRhdGEgZnJvbSBtYXRjaGVzCmRlZl9ob21lIDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgc2VsZWN0KGRhdGUsIGhvbWVfdGVhbSwgaG9tZV90ZWFtX2dvYWxrZWVwZXJfc2NvcmUsIGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmUpICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgZ29hbGtlZXBlcl9yYXRpbmcgPSBob21lX3RlYW1fZ29hbGtlZXBlcl9zY29yZSwgbWVhbl9kZWZlbnNlX3JhdGluZyA9IGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmUpCgpkZWZfYXdheSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBhd2F5X3RlYW0sIGF3YXlfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlLCBhd2F5X3RlYW1fbWVhbl9kZWZlbnNlX3Njb3JlKSAlPiUgCiAgcmVuYW1lKHRlYW0gPSBhd2F5X3RlYW0sIGdvYWxrZWVwZXJfcmF0aW5nID0gYXdheV90ZWFtX2dvYWxrZWVwZXJfc2NvcmUsIG1lYW5fZGVmZW5zZV9yYXRpbmcgPSBhd2F5X3RlYW1fbWVhbl9kZWZlbnNlX3Njb3JlKQoKZGVmX3JhdGluZyA8LSBkcm9wX25hKHJiaW5kKGRlZl9ob21lLCBkZWZfYXdheSkpCgojIEdldCBsYXRlc3QgY29tYmluZWQgcmF0aW5nIG9mIGVhY2ggdGVhbSBhbmQgc2hvdyB0b3AgMTAKbGF0ZXN0X2RlZl9yYXRpbmcgPC0KICBkZWZfcmF0aW5nICU+JSAKICBhcnJhbmdlKHRlYW0sIGRlc2MoZGF0ZSkpICU+JSAKICBtdXRhdGUodG90YWxfZGVmID0gZ29hbGtlZXBlcl9yYXRpbmcgKyBtZWFuX2RlZmVuc2VfcmF0aW5nKSAlPiUgCiAgZ3JvdXBfYnkodGVhbSkgJT4lIAogIG11dGF0ZShyb3dfbnVtYmVyID0gcm93X251bWJlcih0ZWFtKSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyPT0xKSAlPiUgCiAgYXJyYW5nZSgtdG90YWxfZGVmKSAlPiUgCiAgc2VsZWN0KC1yb3dfbnVtYmVyLCAtZGF0ZSkKCmdncGxvdChkYXRhID0gaGVhZChsYXRlc3RfZGVmX3JhdGluZywgMTApLCBtYXBwaW5nPWFlcyh4PXRvdGFsX2RlZiwgeT1yZW9yZGVyKHRlYW0sIHRvdGFsX2RlZiksIGxhYmVsPXRvdGFsX2RlZikpICsgCiAgZ2VvbV9jb2woZmlsbD0iIzg4QzBEMCIpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gIlRvcCAxMCB0ZWFtcyB3aXRoIHRoZSBzdHJvbmdlc3QgZGVmZW5zZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiBnb2Fsa2VlcGVyIGFuZCBtZWFuIGRlZmVuc2UgcmF0aW5ncyIsCiAgICAgICB4ID0gIlRvdGFsIERlZmVuc2UgUmF0aW5nIiwKICAgICAgIHkgPSAiVGVhbXMiKSAKYGBgCgojIyMgVGVhbXMgd2l0aCBzdHJvbmdlc3QgbWlkZmllbGQKCmBgYHtyfQptaWRfaG9tZSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBob21lX3RlYW0sIGhvbWVfdGVhbV9tZWFuX21pZGZpZWxkX3Njb3JlKSAlPiUgCiAgcmVuYW1lKHRlYW0gPSBob21lX3RlYW0sIG1pZGZpZWxkX3JhdGluZyA9IGhvbWVfdGVhbV9tZWFuX21pZGZpZWxkX3Njb3JlKQoKbWlkX2F3YXkgPC0KICBpbnB1dF9kYXRhICU+JSAKICBzZWxlY3QoZGF0ZSwgYXdheV90ZWFtLCBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZSkgJT4lIAogIHJlbmFtZSh0ZWFtID0gYXdheV90ZWFtLCBtaWRmaWVsZF9yYXRpbmcgPSBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZSkKCm1pZF9yYXRpbmcgPC0gZHJvcF9uYShyYmluZChtaWRfaG9tZSwgbWlkX2F3YXkpKQoKIyBHZXQgbGF0ZXN0IG1pZGZpZWxkIHJhdGluZyBvZiBlYWNoIHRlYW0gYW5kIHNob3cgdG9wIDEwCmxhdGVzdF9taWRfcmF0aW5nIDwtCiAgbWlkX3JhdGluZyAlPiUgCiAgYXJyYW5nZSh0ZWFtLCBkZXNjKGRhdGUpKSAlPiUgCiAgZ3JvdXBfYnkodGVhbSkgJT4lIAogIG11dGF0ZShyb3dfbnVtYmVyID0gcm93X251bWJlcih0ZWFtKSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyID09IDEpICU+JSAKICBhcnJhbmdlKC1taWRmaWVsZF9yYXRpbmcpICU+JSAKICBzZWxlY3QoLWRhdGUsIC1yb3dfbnVtYmVyKQoKZ2dwbG90KGRhdGEgPSBoZWFkKGxhdGVzdF9taWRfcmF0aW5nLCAxMCksIG1hcHBpbmc9YWVzKHg9bWlkZmllbGRfcmF0aW5nLCB5PXJlb3JkZXIodGVhbSwgbWlkZmllbGRfcmF0aW5nKSwgbGFiZWw9bWlkZmllbGRfcmF0aW5nKSkgKyAKICBnZW9tX2NvbChmaWxsPSAiIzg4QzBEMCIpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gIlRvcCAxMCB0ZWFtcyB3aXRoIHRoZSBzdHJvbmdlc3QgbWlkZmllbGQiLAogICAgICAgc3VidGl0bGUgPSAiQmFzZWQgb24gdGhlIGF2ZXJhZ2UgcmF0aW5nIG9mIHRoZSA0IGhpZ2hlc3QgcmF0ZWQgbWlkZmllbGQgcGxheWVycyBvZiBlYWNoIHRlYW0iLAogICAgICAgeCA9ICJNaWRmaWVsZCBSYXRpbmciLAogICAgICAgeSA9ICJUZWFtcyIpCmBgYAoKIyMjIFRlYW1zIHdpdGggc3Ryb25nZXN0IG9mZmVuc2UKCmBgYHtyfQpvZmZfaG9tZSA8LQogIGlucHV0X2RhdGEgJT4lIAogIHNlbGVjdChkYXRlLCBob21lX3RlYW0sIGhvbWVfdGVhbV9tZWFuX29mZmVuc2Vfc2NvcmUpICU+JSAKICByZW5hbWUodGVhbSA9IGhvbWVfdGVhbSwgb2ZmZW5zZV9yYXRpbmcgPSBob21lX3RlYW1fbWVhbl9vZmZlbnNlX3Njb3JlKQoKb2ZmX2F3YXkgPC0KICBpbnB1dF9kYXRhICU+JSAKICBzZWxlY3QoZGF0ZSwgYXdheV90ZWFtLCBhd2F5X3RlYW1fbWVhbl9vZmZlbnNlX3Njb3JlKSAlPiUgCiAgcmVuYW1lKHRlYW0gPSBhd2F5X3RlYW0sIG9mZmVuc2VfcmF0aW5nID0gYXdheV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZSkKCm9mZl9yYXRpbmcgPC0gZHJvcF9uYShyYmluZChvZmZfaG9tZSwgb2ZmX2F3YXkpKQoKIyBHZXQgbGF0ZXN0IG9mZmVuc2UgcmF0aW5nIG9mIGVhY2ggdGVhbSBhbmQgc2hvdyB0b3AgMTAKbGF0ZXN0X29mZl9yYXRpbmcgPC0KICBvZmZfcmF0aW5nICU+JSAKICBhcnJhbmdlKHRlYW0sIGRlc2MoZGF0ZSkpICU+JSAKICBncm91cF9ieSh0ZWFtKSAlPiUgCiAgbXV0YXRlKHJvd19udW1iZXIgPSByb3dfbnVtYmVyKHRlYW0pKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIgPT0gMSkgJT4lIAogIGFycmFuZ2UoLW9mZmVuc2VfcmF0aW5nKSAlPiUgCiAgc2VsZWN0KC1kYXRlLCAtcm93X251bWJlcikKCmdncGxvdChkYXRhID0gaGVhZChsYXRlc3Rfb2ZmX3JhdGluZywgMTApLCBtYXBwaW5nPWFlcyh4PW9mZmVuc2VfcmF0aW5nLCB5PXJlb3JkZXIodGVhbSwgb2ZmZW5zZV9yYXRpbmcpLCBsYWJlbD1vZmZlbnNlX3JhdGluZykpICsKICBnZW9tX2NvbChmaWxsPSIjODhDMEQwIikgKwogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGU9IlRvcCAxMCB0ZWFtcyB3aXRoIHRoZSBzdHJvbmdlc3Qgb2ZmZW5zZSIsCiAgICAgIHN1YnRpdGxlPSJCYXNlZCBvbiB0aGUgYXZlcmFnZSByYXRpbmcgb2YgdGhlIDMgaGlnaGVzdCByYXRlZCBvZmZlbnNpdmUgcGxheWVycyBvZiBlYWNoIHRlYW0iLAogICAgICB4PSJPZmZlbnNlIFJhdGluZyIsCiAgICAgIHk9IlRlYW1zIikKYGBgCgojIyBJcyBpdCBiZXR0ZXIgdG8gcGxheSBhdCBob21lID8KCmBgYHtyfQpob21lX3RlYW1fYWR2YW50YWdlIDwtCiAgaW5wdXRfZGF0YSAlPiUgCiAgZmlsdGVyKG5ldXRyYWxfbG9jYXRpb24gPT0gRkFMU0UpICU+JSAKICBjb3VudChob21lX3RlYW1fcmVzdWx0KSAlPiUgCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBsYWJlbF9wZXJjZW50KCkobi9zdW0obikpKQoKZ2dwbG90KGRhdGEgPSBob21lX3RlYW1fYWR2YW50YWdlLCBtYXBwaW5nPWFlcyh4PSIiLCB5PW4sIGZpbGw9aG9tZV90ZWFtX3Jlc3VsdCkpICsKICBnZW9tX2Jhcih3aWR0aCA9IDEsIHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvcj0id2hpdGUiKSArCiAgY29vcmRfcG9sYXIoInkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0VCQ0I4QiIsICIjQkY2MTZBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjQTNCRThDIikpICsKICB0aGVtZV92b2lkKCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIG1hdGNoIHJlc3VsdHMgb2YgaG9tZSB0ZWFtcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJFeGNsdWRpbmcgbWF0Y2hlcyBwbGF5ZWQgYXQgbmV1dHJhbCBsb2NhdGlvbnMiLAogICAgICAgZmlsbD0iUmVzdWx0IikKYGBgCgojIyBDb3JyZWxhdGlvbiBNYXRyaXgKCmBgYHtyfQojIHNlbGVjdCBudW1lcmljIGNvbHVtbnMgb25seQppbnB1dF9udW1lcmljX2RhdGEgPC0gaW5wdXRfZGF0YSAlPiUKICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lCiAgZHJvcF9uYSgpCgojIHJlbmFtZSB2YXJpYWJsZXMgZm9yIGVhc2llciBjb3JyZWxhdGlvbiBwbG90IHZpc3VhbGl6YXRpb24KaW5wdXRfbnVtZXJpY19kYXRhIDwtIGlucHV0X251bWVyaWNfZGF0YSAlPiUgcmVuYW1lKAogIHJhbmsxID0gaG9tZV90ZWFtX2ZpZmFfcmFuaywKICByYW5rMiA9IGF3YXlfdGVhbV9maWZhX3JhbmssCiAgdG90YWxfZmlmYV9wb2ludHMxID0gaG9tZV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzLAogIHRvdGFsX2ZpZmFfcG9pbnRzMiA9IGF3YXlfdGVhbV90b3RhbF9maWZhX3BvaW50cywKICBzY29yZTEgPSBob21lX3RlYW1fc2NvcmUsCiAgc2NvcmUyID0gYXdheV90ZWFtX3Njb3JlLAogIGdrX3Njb3JlMSA9IGhvbWVfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlLAogIGdrX3Njb3JlMiA9IGF3YXlfdGVhbV9nb2Fsa2VlcGVyX3Njb3JlLAogIGRmX3Njb3JlMSA9IGhvbWVfdGVhbV9tZWFuX2RlZmVuc2Vfc2NvcmUsCiAgZGZfc2NvcmUyID0gYXdheV90ZWFtX21lYW5fZGVmZW5zZV9zY29yZSwKICBhdHRfc2NvcmUxID0gaG9tZV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZSwKICBhdHRfc2NvcmUyID0gYXdheV90ZWFtX21lYW5fb2ZmZW5zZV9zY29yZSwKICBtZl9zY29yZTEgPSBob21lX3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZSwKICBtZl9zY29yZTIgPSBhd2F5X3RlYW1fbWVhbl9taWRmaWVsZF9zY29yZQopCgojIGNyZWF0ZSBjb3JyZWxhdGlvbiBwbG90CmlucHV0X251bWVyaWNfZGF0YSAlPiUKICBjb3IoKSAlPiUKICBjb3JycGxvdCgKICAgIHR5cGUgPSAidXBwZXIiLAogICAgZGlhZyA9IEZBTFNFLAogICAgY29sPWNvbG9yUmFtcFBhbGV0dGUoYygiZmlyZWJyaWNrIiwibGlnaHR5ZWxsb3ciLCJncmVlbjQiKSkoMTAwKSwKICAgIG1ldGhvZCA9ICJzaGFkZSIsCiAgICBzaGFkZS5jb2wgPSBOQSwKICAgIHRsLmNvbCA9ICJibGFjayIsCiAgICB0bC5zcnQgPSA0NQogICkKYGBgCgojIDIuIERhdGEgUHJvY2Vzc2luZyAvIEZlYXR1cmUgRW5naW5lZXJpbmcKCiMjIENyZWF0ZSBuZXcgZmVhdHVyZXMKCmBgYHtyfQpvdXRwdXRfZGF0YSA8LSBpbnB1dF9kYXRhCgpvdXRwdXRfZGF0YSRyYW5rX2RpZmYgPC0gb3V0cHV0X2RhdGEkaG9tZV90ZWFtX2ZpZmFfcmFuayAtIG91dHB1dF9kYXRhJGF3YXlfdGVhbV9maWZhX3JhbmsKCm91dHB1dF9kYXRhJGF2ZXJhZ2VfcmFuayA8LSAob3V0cHV0X2RhdGEkaG9tZV90ZWFtX2ZpZmFfcmFuayArIG91dHB1dF9kYXRhJGF3YXlfdGVhbV9maWZhX3JhbmspLzIKCm91dHB1dF9kYXRhJHBvaW50X2RpZmYgPC0gb3V0cHV0X2RhdGEkaG9tZV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzIC0gb3V0cHV0X2RhdGEkYXdheV90ZWFtX3RvdGFsX2ZpZmFfcG9pbnRzCgpvdXRwdXRfZGF0YSRzY29yZV9kaWZmIDwtIG91dHB1dF9kYXRhJGhvbWVfdGVhbV9zY29yZSAtIG91dHB1dF9kYXRhJGF3YXlfdGVhbV9zY29yZQoKb3V0cHV0X2RhdGEkd2luIDwtIG91dHB1dF9kYXRhJHNjb3JlX2RpZmYgPiAwCgpvdXRwdXRfZGF0YSRzdGFrZSA8LSBvdXRwdXRfZGF0YSR0b3VybmFtZW50ICE9ICdGcmllbmRseScKYGBgCgojIyBNb2RlbAoKYGBge3J9CiMgY3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0IHNldApzYW1wbGUgPC0KICBzYW1wbGUoYyhUUlVFLCBGQUxTRSksCiAgICAgICAgIG5yb3cob3V0cHV0X2RhdGEpLAogICAgICAgICByZXBsYWNlID0gVFJVRSwKICAgICAgICAgcHJvYiA9IGMoMC43LCAwLjMpKQp0cmFpbiA8LSBvdXRwdXRfZGF0YVtzYW1wbGUsXQp0ZXN0IDwtIG91dHB1dF9kYXRhWyFzYW1wbGUsXQoKIyBmaXQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApsb2dyZWcgPC0KICBnbG0od2luIH4gYXZlcmFnZV9yYW5rICsgcmFua19kaWZmICsgcG9pbnRfZGlmZiwKICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgZGF0YSA9IHRyYWluKQpzdW1tYXJ5KGxvZ3JlZykKCiMgY2FsYyBwcm9iYWJpbGl0eSBvZiB3aW4gZm9yIGVhY2ggdGVhbSBpbiB0ZXN0IGRhdGFzZXQKcHJlZGljdGVkIDwtIHByZWRpY3QobG9ncmVnLCB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKCgojIGNvbnZlcnQgd2lucyB0byAxIGFuZCAwCnRlc3Qkd2luIDwtIGlmZWxzZSh0ZXN0JHdpbiA9PSBUUlVFLCAxLCAwKQoKIyBmaW5kIG9wdGltYWwgY3V0b2ZmIHByb2JhYmlsaXR5IHRvIHVzZSB0byBtYXhpbWl6ZSBhY2N1cmFjeQpvcHRpbWFsIDwtIG9wdGltYWxDdXRvZmYodGVzdCR3aW4sIHByZWRpY3RlZClbMV0Kb3B0aW1hbAoKIyBjb25mdXNpb24gbWF0cml4CmNvbmZ1c2lvbk1hdHJpeCh0ZXN0JHdpbiwgcHJlZGljdGVkKQoKIyBjYWxjdWxhdGUgbWlzcyBjbGFzc2lmaWNhdGlvbiBlcnJvciByYXRlCm1pc0NsYXNzRXJyb3IodGVzdCR3aW4sIHByZWRpY3RlZCwgdGhyZXNob2xkID0gb3B0aW1hbCkKCiMgUk9DCnBsb3RST0ModGVzdCR3aW4sIHByZWRpY3RlZCkKCmBgYAoKIyMgVGVzdAoKYGBge3J9CndjX3RlYW1zIDwtIGxpc3QoJ1FhdGFyJywgJ0VjdWFkb3InLCAnU2VuZWdhbCcsICdOZXRoZXJsYW5kcycsICdFbmdsYW5kJywgJ0lyYW4nLCAnVVNBJywKICAgICAgICAgICAgICAgICAgJ1dhbGVzJywgJ0FyZ2VudGluYScsICdTYXVkaSBBcmFiaWEnLCAnTWV4aWNvJywgJ1BvbGFuZCcsICdGcmFuY2UnLCAKICAgICAgICAgICAgICAgICAgJ0F1c3RyYWxpYScsICdEZW5tYXJrJywgJ1R1bmlzaWEnLCAnU3BhaW4nLCAnQ29zdGEgUmljYScsICdHZXJtYW55JywgCiAgICAgICAgICAgICAgICAgICdKYXBhbicsICdCZWxnaXVtJywgJ0NhbmFkYScsICdNb3JvY2NvJywgJ0Nyb2F0aWEnLCAnQnJhemlsJywgJ1NlcmJpYScsIAogICAgICAgICAgICAgICAgICAnU3dpdHplcmxhbmQnLCAnQ2FtZXJvb24nLCAnUG9ydHVnYWwnLCAnR2hhbmEnLCAnVXJ1Z3VheScsICdTb3V0aCBLb3JlYScpCgp3Y19yYW5raW5nc19ob21lIDwtIG91dHB1dF9kYXRhICU+JSAKICBmaWx0ZXIoZGF0ZT4iMjAxMy0wMS0wMSIpICU+JSAKICBzZWxlY3QoaG9tZV90ZWFtLCBob21lX3RlYW1fZmlmYV9yYW5rLCBob21lX3RlYW1fdG90YWxfZmlmYV9wb2ludHMpICU+JSAKICBmaWx0ZXIoaG9tZV90ZWFtICVpbiUgd2NfdGVhbXMpCiAgCndjX3JhbmtpbmdzX2F3YXkgPC0gb3V0cHV0X2RhdGEgJT4lIAogIGZpbHRlcihkYXRlPiIyMDEzLTAxLTAxIikgJT4lIAogIHNlbGVjdChhd2F5X3RlYW0sIGF3YXlfdGVhbV9maWZhX3JhbmssIGF3YXlfdGVhbV90b3RhbF9maWZhX3BvaW50cykgJT4lIAogIGZpbHRlcihhd2F5X3RlYW0gJWluJSB3Y190ZWFtcykKYGBgCgpgYGB7cn0KIyBwcmVwYXJlIGxpc3RzCnNpbXVsYXRpb25fd2lubmVycyA8LSBsaXN0KCkKc2ltdWxhdGlvbl9yZXN1bHRzX3dpbm5lcnMgPC0gbGlzdCgpCnNpbXVsYXRpb25fcmVzdWx0c19yb3VuZDE2IDwtIGxpc3QoKQpzaW11bGF0aW9uX2RmX3JvdW5kMTYgPC0gbGlzdCgpCnNpbXVsYXRpb25fcmVzdWx0c19xdWFydGVyZmluYSA8LSBsaXN0KCkKc2ltdWxhdGlvbl9kZl9xdWFydGVyZmluYWwgPC0gbGlzdCgpCnNpbXVsYXRpb25fcmVzdWx0c19zZW1pZmluYSA8LSBsaXN0KCkKc2ltdWxhdGlvbl9kZl9zZW1pZmluYWwgPC0gbGlzdCgpCgojIHNpbXVsYXRpb25zCm4gPSAxMDAwCgojIHNlbGVjdCB3aG8gd2lsbCBjb21lIG91dCBvZiB0aGUgZ3JvdXAgc3RhZ2VzCmNhbmRpZGF0ZXMgPC0KICBsaXN0KAogICAgJ1NlbmVnYWwnLAogICAgJ05ldGhlcmxhbmRzJywKICAgICdFbmdsYW5kJywKICAgICdVU0EnLAogICAgJ0FyZ2VudGluYScsCiAgICAnUG9sYW5kJywKICAgICdGcmFuY2UnLAogICAgJ0Rlbm1hcmsnLAogICAgJ1NwYWluJywKICAgICdHZXJtYW55JywKICAgICdCZWxnaXVtJywKICAgICdDcm9hdGlhJywKICAgICdCcmF6aWwnLAogICAgJ1NlcmJpYScsCiAgICAnUG9ydHVnYWwnLAogICAgJ1VydWd1YXknCiAgKQpmaW5hbHMgPSBsaXN0KCdyb3VuZF9vZl8xNicsICdxdWFydGVyZmluYWwnLCAnc2VtaWZpbmFsJywgJ2ZpbmFsJykKCiMgc2ltdWxhdGUKCmZvciAoZiBpbiBmaW5hbHMpewogIGl0ZXJhdGlvbnMgPC0gbGVuZ3RoKGNhbmRpZGF0ZXMpLzIKICB3aW5uZXJzID0gbGlzdCgpCiAgcHJvYiA9IGxpc3QoKQogIAogIGZvciAoaSBpbiByYW5nZShpdGVyYXRpb25zKSl7CiAgICBob21lIDwtIGNhbmRpZGF0ZXNbaSoyXQogICAgYXdheSA8LSBjYW5kaWRhdGVzW2kqMisxXQogICAgCiAgICByb3cgPC0gZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IDAsIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVzdCkpKSkKICAgIGNvbG5hbWVzKHJvdykgPC0gY29sbmFtZXModGVzdCkKICAgIAogICAgaG9tZV9yYW5rIDwtIHdjX3JhbmtpbmdzX2hvbWUgJT4lIGZpbHRlcgogICAgCiAgICAKICAgIAogICAgCiAgfQp9CiAgCgoKCgoKCgoKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCg==